#ifdef PBC_CLASS_LITERAL_NEW

// static
template <typename IsolateT>
Handle<ClassBoilerplate> ClassBoilerplate::New(
    IsolateT* isolate, const portable::PbcScript& pbc_script,
    portable::PbcClassLiteral expr, AllocationType allocation) {
  // Create a non-caching handle scope to ensure that the temporary handle used
  // by ObjectDescriptor for passing Smis around does not corrupt handle cache
  // in CanonicalHandleScope.
  typename IsolateT::HandleScopeType scope(isolate);
  auto* factory = isolate->factory();
  ObjectDescriptor<IsolateT> static_desc(kMinimumClassPropertiesCount);
  ObjectDescriptor<IsolateT> instance_desc(kMinimumPrototypePropertiesCount);

  for (uint64_t i = 0; i < expr.getNumberOfProperties(); i++) {
    portable::PbcClassProperty property = expr.getProperty(i);
    ObjectDescriptor<IsolateT>& desc =
        property.isStatic() ? static_desc : instance_desc;
    if (property.isComputedName()) {
      if (property.kind() != portable::ClassPropertyKind::FIELD) {
        desc.IncComputedCount();
      }
    } else {
      if (property.key().Kind() == portable::PbcValueKind::kU8String ||
          property.key().Kind() == portable::PbcValueKind::kU16String) {
        desc.IncPropertiesCount();
      } else {
        desc.IncElementsCount();
      }
    }
  }

  //
  // Initialize class object template.
  //
  static_desc.CreateTemplates(isolate);
  static_assert(JSFunction::kLengthDescriptorIndex == 0);
  {
    // Add length_accessor.
    PropertyAttributes attribs =
        static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY);
    static_desc.AddConstant(isolate, factory->length_string(),
                            factory->function_length_accessor(), attribs);
  }
  {
    // Add name_accessor.
    // All classes, even anonymous ones, have a name accessor.
    PropertyAttributes attribs =
        static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY);
    static_desc.AddConstant(isolate, factory->name_string(),
                            factory->function_name_accessor(), attribs);
  }
  {
    // Add prototype_accessor.
    PropertyAttributes attribs =
        static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE | READ_ONLY);
    static_desc.AddConstant(isolate, factory->prototype_string(),
                            factory->function_prototype_accessor(), attribs);
  }
  {
    Handle<ClassPositions> class_positions = factory->NewClassPositions(
        expr.getStartPosition(), expr.getEndPosition());
    static_desc.AddConstant(isolate, factory->class_positions_symbol(),
                            class_positions, DONT_ENUM);
  }

  //
  // Initialize prototype object template.
  //
  instance_desc.CreateTemplates(isolate);
  {
    Handle<Object> value(
        Smi::FromInt(ClassBoilerplate::kConstructorArgumentIndex), isolate);
    instance_desc.AddConstant(isolate, factory->constructor_string(), value,
                              DONT_ENUM);
  }

  //
  // Fill in class boilerplate.
  //
  int dynamic_argument_index = ClassBoilerplate::kFirstDynamicArgumentIndex;

  for (uint64_t i = 0; i < expr.getNumberOfProperties(); i++) {
    portable::PbcClassProperty property = expr.getProperty(i);
    ClassBoilerplate::ValueKind value_kind;
    int value_index = dynamic_argument_index;
    switch (property.kind()) {
      case portable::ClassPropertyKind::METHOD:
        value_kind = ClassBoilerplate::kData;
        break;
      case portable::ClassPropertyKind::GETTER:
        value_kind = ClassBoilerplate::kGetter;
        break;
      case portable::ClassPropertyKind::SETTER:
        value_kind = ClassBoilerplate::kSetter;
        break;
      case portable::ClassPropertyKind::FIELD:
        DCHECK_IMPLIES(property.isComputedName(), !property.isPrivate());
        if (property.isComputedName()) {
          ++dynamic_argument_index;
        }
        continue;
      case portable::ClassPropertyKind::AUTO_ACCESSOR:
#if V8_MAJOR_VERSION == 11
        UNREACHABLE();
#else

        value_kind = ClassBoilerplate::kAutoAccessor;
        // Auto-accessors have two arguments (getter and setter).
        ++dynamic_argument_index;
#endif
    }

    ObjectDescriptor<IsolateT>& desc =
        property.isStatic() ? static_desc : instance_desc;
    if (property.isComputedName()) {
      int computed_name_index = value_index;
      dynamic_argument_index += 2;  // Computed name and value indices.
      desc.AddComputed(value_kind, computed_name_index);
      continue;
    }
    dynamic_argument_index++;

    portable::PbcValue key_literal = property.key();
    switch (key_literal.Kind()) {
      case portable::PbcValueKind::kSmi: {
        uint32_t index =
            static_cast<const portable::SmiScalar&>(key_literal).GetValue();
        desc.AddIndexedProperty(isolate, index, value_kind, value_index);
        break;
      }
      case portable::PbcValueKind::kU8String: {
        std::string_view str = pbc_script.GetString(
            static_cast<const portable::StringRef&>(key_literal));
        Handle<String> name = isolate->factory()->InternalizeString(
            v8::base::Vector<const uint8_t>(
                reinterpret_cast<const uint8_t*>(str.data()), str.size()));
        desc.AddNamedProperty(isolate, name, value_kind, value_index);
        break;
      }
      case portable::PbcValueKind::kU16String: {
        std::u16string_view str = pbc_script.GetU16String(
            static_cast<const portable::StringRef&>(key_literal));
        Handle<String> name = isolate->factory()->InternalizeString(
            v8::base::Vector<const uint16_t>(
                reinterpret_cast<const uint16_t*>(str.data()), str.size()));
        desc.AddNamedProperty(isolate, name, value_kind, value_index);
        break;
      }
      default:
        FATAL("unsupported PbcValue kind");
    }
  }

  static_desc.Finalize(isolate);
  instance_desc.Finalize(isolate);

#if V8_MAJOR_VERSION == 11
  auto result = Handle<ClassBoilerplate>::cast(
      factory->NewFixedArray(kBoilerplateLength, AllocationType::kOld));
#else
  auto result = Cast<ClassBoilerplate>(
      factory->NewStruct(CLASS_BOILERPLATE_TYPE, allocation));
#endif

  result->set_arguments_count(dynamic_argument_index);

  result->set_static_properties_template(*static_desc.properties_template());
  result->set_static_elements_template(*static_desc.elements_template());
  result->set_static_computed_properties(*static_desc.computed_properties());

  result->set_instance_properties_template(
      *instance_desc.properties_template());
  result->set_instance_elements_template(*instance_desc.elements_template());
  result->set_instance_computed_properties(
      *instance_desc.computed_properties());

  return scope.CloseAndEscape(result);
}

template Handle<ClassBoilerplate> ClassBoilerplate::New(
    Isolate* isolate, const portable::PbcScript& pbc_script,
    portable::PbcClassLiteral expr, AllocationType allocation);
template Handle<ClassBoilerplate> ClassBoilerplate::New(
    LocalIsolate* isolate, const portable::PbcScript& pbc_script,
    portable::PbcClassLiteral expr, AllocationType allocation);

#endif